package com.hemerasolutions.v2trainer.service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;

import org.hibernate.exception.ConstraintViolationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.hemerasolutions.v2trainer.dao.ExceptionDao;
import com.hemerasolutions.v2trainer.dao.interfaces.BodyRegionDaoInterface;
import com.hemerasolutions.v2trainer.dao.interfaces.ExerciseDaoInterface;
import com.hemerasolutions.v2trainer.dao.interfaces.ExerciseGroupDaoInterface;
import com.hemerasolutions.v2trainer.dao.interfaces.ModalityDaoInterface;
import com.hemerasolutions.v2trainer.dao.interfaces.PhotoDaoInterface;
import com.hemerasolutions.v2trainer.dao.interfaces.RpeDaoInterface;
import com.hemerasolutions.v2trainer.dao.interfaces.StepDaoInterface;
import com.hemerasolutions.v2trainer.dao.interfaces.TrainingTypeDaoInterface;
import com.hemerasolutions.v2trainer.dao.interfaces.VersionableModalityDaoInterface;
import com.hemerasolutions.v2trainer.dao.interfaces.VideoDaoInterface;
import com.hemerasolutions.v2trainer.model.exercise.Exercise;
import com.hemerasolutions.v2trainer.model.exercise.executions.Execution;
import com.hemerasolutions.v2trainer.model.exercise.intensity.IntensityModZone;
import com.hemerasolutions.v2trainer.model.exercise.intensity.IntensityZone;
import com.hemerasolutions.v2trainer.model.exerciseGroup.ExerciseGroup;
import com.hemerasolutions.v2trainer.model.modality.Modality;
import com.hemerasolutions.v2trainer.model.modality.VersionableModality;
import com.hemerasolutions.v2trainer.model.multimedia.Photo;
import com.hemerasolutions.v2trainer.model.multimedia.Video;
import com.hemerasolutions.v2trainer.model.muscle.BodyRegion;
import com.hemerasolutions.v2trainer.model.screen.Reference;
import com.hemerasolutions.v2trainer.model.step.Step;
import com.hemerasolutions.v2trainer.model.training.Training;
import com.hemerasolutions.v2trainer.model.training.TrainingType;
import com.hemerasolutions.v2trainer.model.unit.intensity.Rpe;
import com.hemerasolutions.v2trainer.model.unit.volume.VolumeType;
import com.hemerasolutions.v2trainer.service.interfaces.ModalityServiceInterface;


@Service("modalityService")
public class ModalityService extends BaseService implements ModalityServiceInterface {

	private static final long serialVersionUID = 1L;

	@Autowired
	private ModalityDaoInterface modalityDao;

	@Autowired
	private VersionableModalityDaoInterface vModalityDao;
	
	@Autowired
	private TrainingTypeDaoInterface ttdao;
	
	@Autowired
	private BodyRegionDaoInterface brDao;
	
	@Autowired
	private VideoDaoInterface videoDao;

	@Autowired
	private PhotoDaoInterface photoDao;

	@Autowired
	private ExerciseDaoInterface exerciseDao;

	@Autowired
	private RpeDaoInterface rpeDao;

	@Autowired
	private StepDaoInterface stepDao;

	@Autowired
	private ExerciseGroupDaoInterface egDao;
	
	public ModalityService() {
		super();
	}
	
	@PostConstruct
	public void init(){
	}
	

	@Override
	public boolean hasChanged(Modality modality){
		return modalityDao.hasChanged(modality);
	}

	@Override
	public List<Reference> getReferences(Modality modality){
		return modalityDao.getReferences(modality);
	}
	
	
	@Override
	public List<Rpe> listAllRpes(){
		return rpeDao.list();
	}

	@Override
	public List<TrainingType> listTrainingTypes() {

		return ttdao.list();
	}

	@Override
	public Modality saveModality(Modality modality) throws ExceptionService {
		try {
			this.canModalityBeChanged(modality);
			return modalityDao.save(modality);
		} catch (ExceptionDao e) {
			e.printStackTrace();
			throw new ExceptionService(e);
		} catch (ExceptionService e) {
			throw e;
		}
	}

	private void canVersionableModalityBeChanged(VersionableModality vModality) throws ExceptionService {

		ExceptionService ex = new ExceptionService();

		for(Modality mod:vModality.getModalityVersions()){
			try{
				this.canModalityBeChanged(mod);
			}catch(ExceptionService ex2){
				ex.getMessages().addAll(ex2.getMessages());
			}
		}
		
		if(!ex.getMessages().isEmpty()){
			throw ex;
		}
		
	}
	
	private void canModalityBeChanged(Modality modality) throws ExceptionService {

		List<Training> trainings = null;
		List<ExerciseGroup> exerciseGroups = null;
		ExceptionService ex = new ExceptionService();
		
		
		if(modality.hasChanged(modalityDao.getById(modality.getId()))){

			trainings = this.trainingsUsing(modality);
			exerciseGroups = this.exerciseGroupsUsing(modality);
			
			if( !trainings.isEmpty()){
				for(Training training:trainings){
					if(training.isTrainingSystem()){
						ex.getMessages().add(this.localizedText("Modality") + " " + modality.getName() + " " + this.localizedText("Version") + ":" + modality.getVersion() + " " +
								this.localizedText("InUseBy") + " " + this.localizedText("TrainingSystem") + " " + 
								training.getName() + " " + this.localizedText("Version") + ":" + training.getVersion());
					}else{
						ex.getMessages().add(this.localizedText("Modality") + " " + modality.getName() + " " + this.localizedText("Version") + ":" + modality.getVersion() + " " +
								this.localizedText("InUseBy") + " " + this.localizedText("Training") + " " + 
								training.getName() + " " + this.localizedText("Version") + ":" + training.getVersion());
					}
				}
			}
			
			if( !exerciseGroups.isEmpty()){
				for(ExerciseGroup eg:exerciseGroups){
					ex.getMessages().add(this.localizedText("Modality") + " " + modality.getName() + " " + this.localizedText("Version") + ":" + modality.getVersion() + " " +
							this.localizedText("InUseBy") + " " + this.localizedText("ExerciseGroup") + " " + 
							eg.getName() + " " + this.localizedText("Version") + ":" + eg.getVersion());
				}
			}
		}
		
		if(!ex.getMessages().isEmpty()){
			throw ex;
		}
	}

	private List<ExerciseGroup> exerciseGroupsUsing(Modality modality) {
		
		List<ExerciseGroup> exerciseGroups = new ArrayList<ExerciseGroup>();
		Map<String,Object> fields = new HashMap<String,Object>();
		
		fields.put("modality", modality);

		exerciseGroups = egDao.searchExerciseGroups(fields);
		
		return exerciseGroups;
	}

	@Override
	public VersionableModality createModality(VersionableModality vModality) {
		try {
			vModality = vModalityDao.create(vModality);
		} catch (ExceptionDao e) {
			throw new ExceptionService(e);
		} catch (ExceptionService e) {
			throw e;
		}
	
		return vModality;
	}
	
	@Override
	public Modality createModality(Modality modality) {
		try {

			modality = modalityDao.create(modality);

		} catch (ExceptionDao e) {
			e.printStackTrace();
			throw new ExceptionService(e);
		} catch (ExceptionService e) {
			throw e;
		}
	
		return modality;
	}

	@Override
	public Modality readModality(Modality modality) {
		return modalityDao.getById(modality.getId());
	}

	

	@Override
	public void deleteModality(Modality modality) throws ExceptionService{
		try{
			this.canModalityBeChanged(modality);
			VersionableModality vm = modality.getVersionableModality();
			vm.getModalityVersions().remove(modality);
			
			if(vm.getModalityVersions().isEmpty()){
				vModalityDao.delete(vm);
			}else{
				vModalityDao.save(vm);
			}
			
		} catch (ExceptionDao e) {
			e.printStackTrace();
			throw new ExceptionService(e);
		} catch(org.springframework.dao.DataIntegrityViolationException ex){
			ex.printStackTrace();
			throw new ExceptionService(ex);
		} catch(ConstraintViolationException ex2){
			ex2.printStackTrace();
			throw new ExceptionService(ex2);
		}
	}
	
	@Override
	public void deleteVersionableModality(VersionableModality versionableModality) throws ExceptionService {
		try{
			this.canVersionableModalityBeChanged(versionableModality);
			vModalityDao.delete(versionableModality);
		} catch (ExceptionDao e) {
			e.printStackTrace();
			throw new ExceptionService(e);
		} catch(org.springframework.dao.DataIntegrityViolationException ex){
			ex.printStackTrace();
			throw new ExceptionService(ex);
		} catch(ConstraintViolationException ex2){
			ex2.printStackTrace();
			throw new ExceptionService(ex2);
		}
		
	}	
	
	@Override
	public List<BodyRegion> listBodyRegions() {
		return this.brDao.list();
	}

	@Override
	public Photo readDefaultExercisePhoto() {
		Photo photo, photo2;
		photo = photoDao.findByLocation("default_exercise.jpg"); 

		photo2 = new Photo();
		photo2.setHeight(photo.getHeight());
		photo2.setWidth(photo.getWidth());
		photo2.setLocation(photo.getLocation());
		photo2.setName(photo.getName());
		
		return photo2;
	}

	@Override
	public Video readDefaultVideo() {
		Video video, video2;
		video = videoDao.getByName("v2trainer_default_exercise");
		video2 = new Video(video);
		video2.setName("");

		return video2;
	}

	@Override
	public List<Modality> searchModalitys(Map<String, Object> fields) {

		return this.modalityDao.search(fields);
	}


	@Override
	public List<VersionableModality> searchVersionableModalitys(Map<String, Object> fields) {

		return this.vModalityDao.search(fields);
	}

	
	@Override
	public List<BodyRegion> listBodyRegion() {
		return brDao.list();
	}

	
	@Override
	public List<Exercise> searchExercises(Map<String, Object> fields) {
		return exerciseDao.search(fields);
	}

	public Exercise getExerciseById(int id){
		return exerciseDao.getById(id);
	}
	
	@Override
	public List<Training> trainingsUsing(Exercise exercise){
		
		List<Training> trainings = new ArrayList<Training>();
		Map<String,Object> fields = new HashMap<String,Object>();
		
		fields.put("exercise", exercise);

		List<Step> steps = stepDao.searchSteps(fields);
		
		for(Step step:steps){
			if(!trainings.contains(step.getTrainingStep().getTraining())){
				trainings.add(step.getTrainingStep().getTraining());
			}
		}
		
		return trainings;
		
	}
	
	@Override
	public List<Training> trainingsUsingExercises(List<Exercise> exercises){
		
		List<Training> trainings = new ArrayList<Training>();
		Map<String,Object> fields = new HashMap<String,Object>();
		
		fields.put("exercises", exercises);

		List<Step> steps = stepDao.searchSteps(fields);
		
		for(Step step:steps){
			if(!trainings.contains(step.getTrainingStep().getTraining())){
				trainings.add(step.getTrainingStep().getTraining());
			}
		}
		
		return trainings;
		
	}
	
	@Override
	public List<Training> trainingsUsing(Modality modality){
		
		List<Training> trainings = new ArrayList<Training>();
		Map<String,Object> fields = new HashMap<String,Object>();
		
		fields.put("modality", modality);

		List<Step> steps = stepDao.searchSteps(fields);
		
		for(Step step:steps){
			if(!trainings.contains(step.getTrainingStep().getTraining())){
				trainings.add(step.getTrainingStep().getTraining());
			}
		}
		
		return trainings;
		
	}	

	@Override
	public List<Training> trainingsUsing(IntensityZone iz){
		
		List<Training> trainings = new ArrayList<Training>();
		Map<String,Object> fields = new HashMap<String,Object>();
		
		fields.put("intensityZone", iz);

		List<Step> steps = stepDao.searchSteps(fields);
		
		for(Step step:steps){
			if(!trainings.contains(step.getTrainingStep().getTraining())){
				trainings.add(step.getTrainingStep().getTraining());
			}
		}
		
		return trainings;	
	}
	
	@Override
	public List<Training> trainingsUsingIzs(List<IntensityZone> izs){
		
		List<Training> trainings = new ArrayList<Training>();
		Map<String,Object> fields = new HashMap<String,Object>();
		
		fields.put("intensityZones", izs);

		List<Step> steps = stepDao.searchSteps(fields);
		
		for(Step step:steps){
			if(!trainings.contains(step.getTrainingStep().getTraining())){
				trainings.add(step.getTrainingStep().getTraining());
			}
		}
		
		return trainings;	
	}	
	
	@Override
	public List<Training> trainingsUsing(IntensityModZone imz){
		
		
		List<Training> trainings = new ArrayList<Training>();
		Map<String,Object> fields = new HashMap<String,Object>();
		
		fields.put("intensityModZone", imz);

		List<Step> steps = stepDao.searchSteps(fields);
		
		for(Step step:steps){
			if(!trainings.contains(step.getTrainingStep().getTraining())){
				trainings.add(step.getTrainingStep().getTraining());
			}
		}
		
		return trainings;	
	}

	@Override
	public List<Training> trainingsUsing(Modality modality,Execution ex){
		
		List<Training> trainings = new ArrayList<Training>();
		Map<String,Object> fields = new HashMap<String,Object>();
		
		fields.put("modality", modality);
		fields.put("execution", ex);

		List<Step> steps = stepDao.searchSteps(fields);
		
		for(Step step:steps){
			if(!trainings.contains(step.getTrainingStep().getTraining())){
				trainings.add(step.getTrainingStep().getTraining());
			}
		}
		
		return trainings;	
	}
	
	@Override
	public List<Training> trainingsUsing(Modality modality,VolumeType vt){
		
		List<Training> trainings = new ArrayList<Training>();
		Map<String,Object> fields = new HashMap<String,Object>();
		
		fields.put("modality", modality);
		fields.put("volumetType", vt);

		List<Step> steps = stepDao.searchSteps(fields);
		
		for(Step step:steps){
			if(!trainings.contains(step.getTrainingStep().getTraining())){
				trainings.add(step.getTrainingStep().getTraining());
			}
		}
		
		return trainings;	
	}

	@Override
	public VersionableModality saveVersionableModality(VersionableModality versionableModality) {
		try {
			return vModalityDao.save(versionableModality);
		} catch (ExceptionDao e) {
			e.printStackTrace();
			throw new ExceptionService(e);
		} catch (ExceptionService e) {
			throw e;
		}
		
		
	}

	@Override
	public VersionableModality createVersionableModality(
			VersionableModality versionableModality) {

		try {
			return vModalityDao.create(versionableModality);
		} catch (ExceptionDao e) {
			throw new ExceptionService(e);
		} catch (ExceptionService e) {
			throw e;
		}

	}

	@Override
	public VersionableModality readVersionableModality(
			VersionableModality versionableModality) {

		return vModalityDao.getById(versionableModality.getId());
}
	
}
